<template>
	<view class="e-swiper" :style="{
			backgroundColor: props.bgColor,
			height: addUnit( props.height),
			borderRadius: addUnit( props.radius)
		}">
		<view class="e-swiper__loading" v-if="props.loading">
			<e-loading-icon mode="circle"></e-loading-icon>
		</view>
		<swiper v-else class="e-swiper__wrapper" :style="{
				height: addUnit(props.height),
				flex:1
			}" @change="change" :circular="props.circular" :interval="props.interval" :duration="props.duration"
			:autoplay="props.autoplay" :current="props.current" :currentItemId="currentItemId"
			:previousMargin="addUnit(props.previousMargin)" :nextMargin="addUnit(props.nextMargin)"
			:acceleration="props.acceleration" :displayMultipleItems="props.displayMultipleItems"
			:easingFunction="props.easingFunction">
			<swiper-item class="e-swiper__wrapper__item" v-for="(item, index) in props.list" :key="index">
				<view class="e-swiper__wrapper__item__wrapper" :style="[itemStyle(index)]">
					<!-- 在nvue中，image图片的宽度默认为屏幕宽度，需要通过flex:1撑开，另外必须设置高度才能显示图片 -->
					<image class="e-swiper__wrapper__item__wrapper__image" v-if="getItemType(item) === 'image'"
						:src="getSource(item)" :mode="props.imgMode" @tap="clickHandler(index)" :style="{
							height: addUnit(props.height),
							borderRadius: addUnit(props.radius)
						}"></image>

					<video class="e-swiper__wrapper__item__wrapper__video" v-if="getItemType(item) === 'video'"
						:id="`video-${index}`" :enable-progress-gesture="false" :src="getSource(item)"
						:poster="getPoster(item)"
						:title="props.showTitle && isObj(item) && item.title ? item.title : ''" :style="{
							height: addUnit(props.height)
						}" :controls="true" @tap="clickHandler(index)"></video>
					<text v-if="props.showTitle && isObj(item) && item.title && isImage(getSource(item))"
						class="e-swiper__wrapper__item__wrapper__title e-line-1">{{ item.title }}</text>
				</view>
			</swiper-item>
		</swiper>
		<view class="e-swiper__indicator" :style="[addStyle(props.indicatorStyle)]">
			<slot name="indicator">
				<swiper-indicator v-if="!props.loading && props.indicator && !props.showTitle"
					:indicatorActiveColor="props.indicatorActiveColor"
					:indicatorInactiveColor="props.indicatorInactiveColor" :length="props.list.length"
					:current="currentIndex" :indicatorMode="props.indicatorMode"></swiper-indicator>
			</slot>
		</view>
	</view>
</template>
<script lang="ts">
	import { defineComponent } from 'vue'
	import mpMixin from '../../libs/mixin/mpMixin'
	/**
		 * Swiper 轮播图
		 * @description 该组件一般用于导航轮播，广告展示等场景,可开箱即用，
		 * @tutorial https://www.uviewui.com/components/swiper.html
		 * @property {Array}			list					轮播图数据
		 * @property {Boolean}			indicator				是否显示面板指示器（默认 false ）
		 * @property {String}			indicatorActiveColor	指示器非激活颜色（默认 '#FFFFFF' ）
		 * @property {String}			indicatorInactiveColor	指示器的激活颜色（默认 'rgba(255, 255, 255, 0.35)' ）
		 * @property {String | Object}	indicatorStyle			指示器样式，可通过bottom，left，right进行定位
		 * @property {String}			indicatorMode			指示器模式（默认 'line' ）
		 * @property {Boolean}			autoplay				是否自动切换（默认 true ）
		 * @property {String | Number}	current					当前所在滑块的 index（默认 0 ）
		 * @property {String}			currentItemId			当前所在滑块的 item-id ，不能与 current 被同时指定
		 * @property {String | Number}	interval				滑块自动切换时间间隔（ms）（默认 3000 ）
		 * @property {String | Number}	duration				滑块切换过程所需时间（ms）（默认 300 ）
		 * @property {Boolean}			circular				播放到末尾后是否重新回到开头（默认 false ）
		 * @property {String | Number}	previousMargin			前边距，可用于露出前一项的一小部分，nvue和支付宝不支持（默认 0 ）
		 * @property {String | Number}	nextMargin				后边距，可用于露出后一项的一小部分，nvue和支付宝不支持（默认 0 ）
		 * @property {Boolean}			acceleration			当开启时，会根据滑动速度，连续滑动多屏，支付宝不支持（默认 false ）
		 * @property {Number}			displayMultipleItems	同时显示的滑块数量，nvue、支付宝小程序不支持（默认 1 ）
		 * @property {String}			easingFunction			指定swiper切换缓动动画类型， 只对微信小程序有效（默认 'default' ）
		 * @property {String}			keyName					list数组中指定对象的目标属性名（默认 'url' ）
		 * @property {String}			imgMode					图片的裁剪模式（默认 'aspectFill' ）
		 * @property {String | Number}	height					组件高度（默认 130 ）
		 * @property {String}			bgColor					背景颜色（默认 	'#f3f4f6' ）
		 * @property {String | Number}	radius					组件圆角，数值或带单位的字符串（默认 4 ）
		 * @property {Boolean}			loading					是否加载中（默认 false ）
		 * @property {Boolean}			showTitle				是否显示标题，要求数组对象中有title属性（默认 false ）
		 * @event {Function(index)}	click	点击轮播图时触发	index：点击了第几张图片，从0开始
		 * @event {Function(index)}	change	轮播图切换时触发(自动或者手动切换)	index：切换到了第几张图片，从0开始
		 * @example	<e-swiper :list="list4" keyName="url" :autoplay="false"></e-swiper>
		 */
	export default defineComponent({
		name: "e-swiper",
		mixins: [mpMixin]
	});
</script>
<script lang="ts" setup>
	import {
		ref,
		watch,
		toRefs,
		getCurrentInstance
	} from "vue"
	import {
		swiperProps,
		swiperEmits
	} from './props'
	import {
		throwError,
		addStyle,
		addUnit
	} from "../../libs/utils/_utils";
	import {
		isString,
		isObj,
		isImage,
		isVideo,
		getProperty
	} from "../../libs/utils";
	import {
		useDefaultProps
	} from '../../libs/hooks'
	import type {
		StyleProperties
	} from "../../libs/tokens";
	import componentsProps, {
		type DefaultProps
	} from "../../libs/config/componentsProps";
	import swiperIndicator from './indicator.vue'

	const {
		swiper: swiperDefaultProps
	} = toRefs(componentsProps);

	const _props = defineProps(swiperProps)

	const props = useDefaultProps<DefaultProps['swiper']>(_props, swiperDefaultProps);
	const emit = defineEmits(swiperEmits)

	const ctx = getCurrentInstance()

	const currentIndex = ref(0)

	watch(() => props.value.current, (val, preVal) => {
		if (val === preVal) return;
		currentIndex.value = val as number; // 和上游数据关联上
	})

	const itemStyle = (index : number) => {
		const style : StyleProperties = {}
		// #ifndef APP-NVUE || MP-TOUTIAO
		// 左右流出空间的写法不支持nvue和头条
		// 只有配置了此二值，才加上对应的圆角，以及缩放
		if (props.value.nextMargin && props.value.previousMargin) {
			style.borderRadius = addUnit(props.value.radius)
			if (index !== currentIndex.value) style.transform = 'scale(0.92)'
		}
		// #endif
		return style
	}

	type itemType = string | Record<string, any>

	const getItemType = (item : itemType) => {
		if (isString(item)) return isVideo(getSource(item)) ? 'video' : 'image'
		if (isObj(item) && props.value.keyName) {
			if (!item.type) return isVideo(getSource(item)) ? 'video' : 'image'
			if (item.type === 'image') return 'image'
			if (item.type === 'video') return 'video'
			return 'image'
		}
	}
	// 获取目标路径，可能数组中为字符串，对象的形式，额外可指定对象的目标属性名keyName
	const getSource = (item : itemType) => {
		if (isString(item)) return item
		if (isObj(item) && props.value.keyName) return getProperty(item, props.value.keyName)
		else throwError('e-swiper', '请按格式传递列表参数')
	}
	// 轮播切换事件
	const change = (e : {
		detail : {
			current : number
		}
	}) => {
		// 当前的激活索引
		const {
			current
		} = e.detail
		pauseVideo(currentIndex.value)
		currentIndex.value = current
		emit('change', e.detail)
	}
	// 切换轮播时，暂停视频播放
	const pauseVideo = (index : number) => {
		const lastItem = getSource(props.value.list[index])
		if (isVideo(lastItem)) {
			// 当视频隐藏时，暂停播放
			const video = uni.createVideoContext(`video-${index}`, ctx)
			video.pause()
		}
	}
	// 当一个轮播item为视频时，获取它的视频海报
	const getPoster = (item : itemType) => {
		return isObj(item) && item.poster ? item.poster : ''
	}
	// 点击某个item
	const clickHandler = (index : number) => {
		emit('click', index)
	}
</script>

<style lang="scss" scoped>
	@import "../../libs/theme/swiper.scss";
</style>